package com.cd2cd.code_template.util.japidoc;

import com.alibaba.fastjson.JSONObject;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.google.common.collect.ImmutableMap;
import freemarker.template.TemplateException;
import io.github.yedaxia.apidocs.*;
import io.github.yedaxia.apidocs.parser.ControllerNode;
import io.github.yedaxia.apidocs.parser.RequestNode;
import io.github.yedaxia.apidocs.parser.SpringControllerParser;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 1、判断api文件是否已经存在，存在进行替换操作，no进行首次生成
 */
public class GenJavaApiMain {

    public static final String API_PATH = "/src/main/resources/public/apiDocs/";

    public static void genJavaApiDoc(List<ControllerNode> nodes, String fileGenPath, String projectName, boolean auto) throws IOException, TemplateException {
        // cp style.css
        String cssStr = JApiTempUtil.fetchResourceByFilePath("/style.css");
        IOUtils.write(cssStr, new FileOutputStream(fileGenPath + "style.css", false), "utf-8");

        // 生成 controllers
        writeOrReplaceControllerFile(nodes, fileGenPath, projectName, auto);

        // 生成 index.html
        writeOrReplaceIndexFile(nodes, fileGenPath, projectName, auto);
    }

    private static void writeOrReplaceControllerFile(List<ControllerNode> nodes, String fileGenPath, String projectName, boolean auto) throws IOException, TemplateException {

        String flag = getFlagStr(!auto);
        Set<String> genHtmlSet = new HashSet<>();
        genHtmlSet.add("index.html");
        String indexTxt = getIndexTxt(fileGenPath, projectName, auto);
        String revertCon = getRangeContent(indexTxt, "<!--catalog-" + flag + "-start-->", "<!--catalog-" + flag + "-end-->");
        String revertJson = getRangeContent(indexTxt, "//--ctrolNode-" + flag + "-start", "//--ctrolNode-" + flag + "-end");

        flag = getFlagStr(auto);
        for (int i = 0; i < nodes.size(); i++) {
            ControllerNode node = nodes.get(i);
            String docFileName = node.getDocFileName();
            String conFilePath = fileGenPath + docFileName + ".html";

            genHtmlSet.add(docFileName + ".html");

            String conTxt;
            if (new File(conFilePath).exists()) {
                conTxt = IOUtils.toString(new FileInputStream(conFilePath), "utf-8");
            } else {
                conTxt = JApiTempUtil.getApiControllerFile(projectName, docFileName, auto);
            }

            // catalog
            conTxt = replaceCatalog(nodes, conTxt, node, auto);

            // action -
            String reqNodeFtl = JApiTempUtil.getApiRequestNodeFile();
            Map<String, Object> dataObj = ImmutableMap.of("requestNodes", node.getRequestNodes());
            String reqNodesHtml = GenFileByFtl.formatTmpFromData(reqNodeFtl, dataObj);

            conTxt = JApiTempUtil.replaceContent(conTxt, "<!--ctrolNode-" + flag + "-start-->", "<!--ctrolNode-" + flag + "-end-->", reqNodesHtml);

            // json
            conTxt = replaceSearchSourceData(nodes, conTxt, auto);

            // replace revert txt 将auto的替换到文件中
            conTxt = replaceRevertTxt(revertCon, revertJson, conTxt, auto);

            IOUtils.write(conTxt, new FileOutputStream(conFilePath, false), "utf-8");
        }

        revertCon = getRangeContent(indexTxt, "<!--catalog-" + flag + "-start-->", "<!--catalog-" + flag + "-end-->");
        revertJson = getRangeContent(indexTxt, "//--ctrolNode-" + flag + "-start", "//--ctrolNode-" + flag + "-end");

        // 替换其他Controller catalog和json
        String[] fs = new File(fileGenPath).list((dir, name) -> name.endsWith(".html") && !genHtmlSet.contains(name));
        for (String f : fs) {
            String cc = fileGenPath + f;
            String conTxt = IOUtils.toString(new FileInputStream(cc), "utf-8");

            // catalog
            ControllerNode node = new ControllerNode();
            node.setDocFileName(f);
            conTxt = replaceCatalog(nodes, conTxt, node, auto);

            // json
            conTxt = replaceSearchSourceData(nodes, conTxt, auto);

            // replace revert txt
            conTxt = replaceRevertTxt(revertCon, revertJson, conTxt, !auto);

            IOUtils.write(conTxt, new FileOutputStream(cc, false), "utf-8");
        }
    }

    private static String replaceRevertTxt(String revertCon, String revertJson, String conTxt, boolean auto) {
        String flag = getFlagStr(!auto);


        conTxt = JApiTempUtil.replaceContent(conTxt, "<!--catalog-" + flag + "-start-->", "<!--catalog-" + flag + "-end-->", revertCon);
        conTxt = JApiTempUtil.replaceContent(conTxt, "//--ctrolNode-" + flag + "-start", "//--ctrolNode-" + flag + "-end", revertJson);

        return conTxt;
    }

    public static String getRangeContent(String res, String s, String e) {
        int sI = res.indexOf(s) + s.length();
        int eI = res.indexOf(e);
        if (sI > -1 && eI > -1) {
            return res.substring(sI, eI);
        }
        return "";
    }

    private static String getIndexTxt(String fileGenPath, String projectName, boolean auto) throws IOException {
        String indexTxt;
        String indexFilePath = fileGenPath + "index.html";
        if (new File(indexFilePath).exists()) {
            indexTxt = IOUtils.toString(new FileInputStream(indexFilePath), "utf-8");
        } else {
            indexTxt = JApiTempUtil.getApiIndexFile(projectName, auto);
        }
        return indexTxt;
    }

    private static void writeOrReplaceIndexFile(List<ControllerNode> nodes, String fileGenPath, String projectName, boolean auto) throws IOException, TemplateException {

        String indexTxt = getIndexTxt(fileGenPath, projectName, auto);
        String indexFilePath = fileGenPath + "index.html";

        indexTxt = replaceCatalog(nodes, indexTxt, null, auto);
        indexTxt = replaceSearchSourceData(nodes, indexTxt, auto);
        indexTxt = replaceIndexController(nodes, indexTxt, auto);

        IOUtils.write(indexTxt, new FileOutputStream(indexFilePath, false), "utf-8");
    }

    private static String replaceIndexController(List<ControllerNode> nodes, String indexTxt, boolean auto) {

        List<String> guide = new ArrayList<>();
        nodes.forEach(n -> {
            guide.add("<a href='" + n.getDocFileName() + ".html' class='list-group-item'>" + n.getDescription() + "</a>");
        });

        String replaceStr = String.join("\n", guide);

        String flag = getFlagStr(auto);
        indexTxt = JApiTempUtil.replaceContent(indexTxt, "<!--ctrolNode-" + flag + "-start-->", "<!--ctrolNode-" + flag + "-end-->", replaceStr);
        return indexTxt;
    }

    private static String replaceSearchSourceData(List<ControllerNode> nodes, String indexTxt, boolean auto) throws IOException, TemplateException {
        List<String> jsons = new ArrayList<>();
        nodes.stream().forEach(node -> {
            node.getRequestNodes().stream().forEach(req -> {
                JSONObject obj = new JSONObject();
                obj.put("name", node.getDescription() + "." + req.getDescription());
                obj.put("url", req.getCodeFileUrl());
                jsons.add(obj.toJSONString());
            });
        });

        String autoJson = String.join(",\n", jsons);

        // replace
        String replaceStr = " /* 无数据 */ ";
        if (StringUtils.isNotBlank(autoJson)) {
            replaceStr = "\n" + autoJson + ", \n";
        }
        String flag = getFlagStr(auto);
        indexTxt = JApiTempUtil.replaceContent(indexTxt, "//--ctrolNode-" + flag + "-start", "//--ctrolNode-" + flag + "-end", replaceStr);
        return indexTxt;
    }

    private static String getFlagStr(boolean auto) {
        if (auto)
            return "auto";
        return "custom";
    }

    private static String replaceCatalog(List<ControllerNode> nodes, String indexTxt, ControllerNode controller, boolean auto) throws IOException, TemplateException {
        // cata-log
        String ftlStr = JApiTempUtil.getApiCommonCatalogFile(2, auto);

        Map<String, Object> dataObj = new HashMap() {{
            put("controllerNodeList", nodes);
        }};
        if (null != controller)
            dataObj.put("controller", controller);

        String formateRes = GenFileByFtl.formatTmpFromData(ftlStr, dataObj);

        String flag = getFlagStr(auto);

        // replace
        indexTxt = JApiTempUtil.replaceContent(indexTxt, "<!--catalog-" + flag + "-start-->", "<!--catalog-" + flag + "-end-->", formateRes);
        return indexTxt;
    }

    private static List<ControllerNode> getLocationController() throws NoSuchFieldException, IllegalAccessException {

        String path = GenJavaApiMain.class.getResource("/").getPath().replace("/target/classes/", "");

        DocsConfig config = new DocsConfig();
        config.setProjectPath(path); // 项目根目录
        config.setProjectName("japi-docs"); // 项目名称
        config.setApiVersion("V1.0");       // 声明该API的版本
        config.setAutoGenerate(Boolean.TRUE);  // 配置自动生成

        DocContext.init(config);

        List<File> controllerFiles = new ArrayList<>();

        File ff = new File(path);


        Utils.wideSearchFile(ff, (f, name) -> f.getName().endsWith(".java") && ParseUtils.compilationUnit(f)
                        .getChildNodesByType(ClassOrInterfaceDeclaration.class)
                        .stream()
                        .anyMatch(cd -> (cd.getAnnotationByName("Controller").isPresent()
                                || cd.getAnnotationByName("RestController").isPresent())
                                && !cd.getAnnotationByName(Ignore.class.getSimpleName()).isPresent()
                                && cd.getAnnotationByName(GenJApidoc.class.getSimpleName()).isPresent()
                        )
                , controllerFiles, false);

        controllerFiles = controllerFiles.stream()
                .collect(
                        Collectors.collectingAndThen(
                                Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(File::getName))), ArrayList::new)
                );

        SpringControllerParser controllerParser = new SpringControllerParser();
        List<ControllerNode> list = new ArrayList<>();
        for (File controllerFile : controllerFiles) {

            ControllerNode controllerNode = controllerParser.parse(controllerFile);

            controllerNode.setSrcFileName(controllerFile.getAbsolutePath());
            controllerNode.setDocFileName(controllerNode.getClassName());
            for (RequestNode requestNode : controllerNode.getRequestNodes()) {
                requestNode.setCodeFileUrl(String.format("%s#%s", controllerNode.getClassName() + ".html", requestNode.getMethodName()));
            }
            list.add(controllerNode);
        }
        return list;
    }

    public static void genJapiFromLocation() throws NoSuchFieldException, IllegalAccessException, IOException, TemplateException {
        List<ControllerNode> controllerNodes = getLocationController();

        String fileGenPath = GenJavaApiMain.class.getResource("/").getPath().replace("/target/classes/", "") + GenJavaApiMain.API_PATH;
        GenJavaApiMain.genJavaApiDoc(controllerNodes, fileGenPath, "", false);
    }

    /**
     * 生成api doc说明：需要在Controller在添加注解 @GenJApidoc
     *
     * @param args
     * @throws NoSuchFieldException
     * @throws IllegalAccessException
     * @throws IOException
     * @throws TemplateException
     */
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, TemplateException {
        genJapiFromLocation();
    }

}
